/*-------------------------------------------------------------------------
 *
 * isn.c
 *	  PostgreSQL 对 ISNs（ISBN、ISMN、ISSN、EAN13、UPC）的类型定义
 *
 * 作者： German Mendez Bravo (Kronuz)
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  contrib/isn/isn.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "EAN13.h"
#include "ISBN.h"
#include "ISMN.h"
#include "ISSN.h"
#include "UPC.h"
#include "fmgr.h"
#include "isn.h"
#include "utils/builtins.h"

PG_MODULE_MAGIC;

#ifdef USE_ASSERT_CHECKING
#define ISN_DEBUG 1
#else
#define ISN_DEBUG 0
#endif

#define MAXEAN13LEN 18

enum isn_type
{
	INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC
};

static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"};

static bool g_weak = false;


/***********************************************************************
 **
 **		用于EAN13/UPC/ISxNs的例程。
 **
 ** 注意：
 ** 在此代码中，标准化字符串是指已知为有效的
 ** ISxN号码，仅包含数字和连字符，并且有足够的空间
 ** 来容纳完整的13位数字加上最多四个连字符。
 ***********************************************************************/

/*----------------------------------------------------------
 * 调试例程。
 *---------------------------------------------------------*/

/*
 * 检查表及其索引是否正确（仅用于调试）
 */
pg_attribute_unused()
static bool fc_check_table(const char *(*fc_TABLE)[2], const unsigned fc_TABLE_index[10][2])
{
	const char *fc_aux1,
			   *fc_aux2;
	int			fc_a,
				fc_b,
				fc_x = 0,
				fc_y = -1,
				fc_i = 0,
				fc_j,
				fc_init = 0;

	if (fc_TABLE == NULL || fc_TABLE_index == NULL)
		return true;

	while (fc_TABLE[fc_i][0] && fc_TABLE[fc_i][1])
	{
		fc_aux1 = fc_TABLE[fc_i][0];
		fc_aux2 = fc_TABLE[fc_i][1];

		/* 必须始终以数字开头： */
		if (!isdigit((unsigned char) *fc_aux1) || !isdigit((unsigned char) *fc_aux2))
			goto invalidtable;
		fc_a = *fc_aux1 - '0';
		fc_b = *fc_aux2 - '0';

		/* 必须始终具有相同的格式和长度： */
		while (*fc_aux1 && *fc_aux2)
		{
			if (!(isdigit((unsigned char) *fc_aux1) &&
				  isdigit((unsigned char) *fc_aux2)) &&
				(*fc_aux1 != *fc_aux2 || *fc_aux1 != '-'))
				goto invalidtable;
			fc_aux1++;
			fc_aux2++;
		}
		if (*fc_aux1 != *fc_aux2)
			goto invalidtable;

		/* 找到了一个新范围 */
		if (fc_a > fc_y)
		{
			/* 检查索引中的当前范围： */
			for (fc_j = fc_x; fc_j <= fc_y; fc_j++)
			{
				if (fc_TABLE_index[fc_j][0] != fc_init)
					goto invalidindex;
				if (fc_TABLE_index[fc_j][1] != fc_i - fc_init)
					goto invalidindex;
			}
			fc_init = fc_i;
			fc_x = fc_a;
		}

		/* 始终获取新限制 */
		fc_y = fc_b;
		if (fc_y < fc_x)
			goto invalidtable;
		fc_i++;
	}

	return true;

invalidtable:
	elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
		 fc_TABLE[fc_i][0], fc_TABLE[fc_i][1], fc_i);
	return false;

invalidindex:
	elog(DEBUG1, "index %d is invalid", fc_j);
	return false;
}

/*----------------------------------------------------------
 * 格式化和转换例程。
 *---------------------------------------------------------*/

static unsigned fc_dehyphenate(char *fc_bufO, char *fc_bufI)
{
	unsigned	fc_ret = 0;

	while (*fc_bufI)
	{
		if (isdigit((unsigned char) *fc_bufI))
		{
			*fc_bufO++ = *fc_bufI;
			fc_ret++;
		}
		fc_bufI++;
	}
	*fc_bufO = '\0';
	return fc_ret;
}

/*
 * 连字符化 --- 尝试将从bufI开始的字符串就地连字符化
 *				  到bufO中，使用给定的连字符范围TABLE。
 *				  假设要使用的输入字符串仅包含数字。
 *
 * 返回实际连字符化的字符数。
 */
static unsigned fc_hyphenate(char *fc_bufO, char *fc_bufI, const char *(*fc_TABLE)[2], const unsigned fc_TABLE_index[10][2])
{
	unsigned	fc_ret = 0;
	const char *fc_ean_aux1,
			   *fc_ean_aux2,
			   *fc_ean_p;
	char	   *fc_firstdig,
			   *fc_aux1,
			   *fc_aux2;
	unsigned	fc_search,
				fc_upper,
				fc_lower,
				fc_step;
	bool		fc_ean_in1,
				fc_ean_in2;

	/* 如果不需要进一步连字符化，则仅压缩字符串 */
	if (fc_TABLE == NULL || fc_TABLE_index == NULL)
	{
		while (*fc_bufI)
		{
			*fc_bufO++ = *fc_bufI++;
			fc_ret++;
		}
		*fc_bufO = '\0';
		return (fc_ret + 1);
	}

	/* 添加剩余的连字符 */

	fc_search = *fc_bufI - '0';
	fc_upper = fc_lower = fc_TABLE_index[fc_search][0];
	fc_upper += fc_TABLE_index[fc_search][1];
	fc_lower--;

	fc_step = (fc_upper - fc_lower) / 2;
	if (fc_step == 0)
		return 0;
	fc_search = fc_lower + fc_step;

	fc_firstdig = fc_bufI;
	fc_ean_in1 = fc_ean_in2 = false;
	fc_ean_aux1 = fc_TABLE[fc_search][0];
	fc_ean_aux2 = fc_TABLE[fc_search][1];
	do
	{
		if ((fc_ean_in1 || *fc_firstdig >= *fc_ean_aux1) && (fc_ean_in2 || *fc_firstdig <= *fc_ean_aux2))
		{
			if (*fc_firstdig > *fc_ean_aux1)
				fc_ean_in1 = true;
			if (*fc_firstdig < *fc_ean_aux2)
				fc_ean_in2 = true;
			if (fc_ean_in1 && fc_ean_in2)
				break;

			fc_firstdig++, fc_ean_aux1++, fc_ean_aux2++;
			if (!(*fc_ean_aux1 && *fc_ean_aux2 && *fc_firstdig))
				break;
			if (!isdigit((unsigned char) *fc_ean_aux1))
				fc_ean_aux1++, fc_ean_aux2++;
		}
		else
		{
			/*
			 * 检查我们应该朝哪个方向移动指针
			 * 并相应调整指针
			 */
			if (*fc_firstdig < *fc_ean_aux1 && !fc_ean_in1)
				fc_upper = fc_search;
			else
				fc_lower = fc_search;

			fc_step = (fc_upper - fc_lower) / 2;
			fc_search = fc_lower + fc_step;

			/* 再次初始化内容： */
			fc_firstdig = fc_bufI;
			fc_ean_in1 = fc_ean_in2 = false;
			fc_ean_aux1 = fc_TABLE[fc_search][0];
			fc_ean_aux2 = fc_TABLE[fc_search][1];
		}
	} while (fc_step);

	if (fc_step)
	{
		fc_aux1 = fc_bufO;
		fc_aux2 = fc_bufI;
		fc_ean_p = fc_TABLE[fc_search][0];
		while (*fc_ean_p && *fc_aux2)
		{
			if (*fc_ean_p++ != '-')
				*fc_aux1++ = *fc_aux2++;
			else
				*fc_aux1++ = '-';
			fc_ret++;
		}
		*fc_aux1++ = '-';
		*fc_aux1 = *fc_aux2;			/* 添加一个前瞻字符 */
		return (fc_ret + 1);
	}
	return fc_ret;
}

/*
 * weight_checkdig -- 接收一个带有标准化ISxN字符串号码的缓冲区，
 *					   以及要加权的长度。
 *
 * 返回该号码的权重（校验位值，0-10）
 */
static unsigned fc_weight_checkdig(char *fc_isn, unsigned fc_size)
{
	unsigned	fc_weight = 0;

	while (*fc_isn && fc_size > 1)
	{
		if (isdigit((unsigned char) *fc_isn))
		{
			fc_weight += fc_size-- * (*fc_isn - '0');
		}
		fc_isn++;
	}
	fc_weight = fc_weight % 11;
	if (fc_weight != 0)
		fc_weight = 11 - fc_weight;
	return fc_weight;
}


/*
 * checkdig --- 接收一个带有标准化ISxN字符串号码的缓冲区，
 *				 以及要检查的长度。
 *
 * 返回校验位值（0-9）
 */
static unsigned fc_checkdig(char *fc_num, unsigned fc_size)
{
	unsigned	fc_check = 0,
				fc_check3 = 0;
	unsigned	fc_pos = 0;

	if (*fc_num == 'M')
	{							/* ISMN以'M'开头 */
		fc_check3 = 3;
		fc_pos = 1;
	}
	while (*fc_num && fc_size > 1)
	{
		if (isdigit((unsigned char) *fc_num))
		{
			if (fc_pos++ % 2)
				fc_check3 += *fc_num - '0';
			else
				fc_check += *fc_num - '0';
			fc_size--;
		}
		fc_num++;
	}
	fc_check = (fc_check + 3 * fc_check3) % 10;
	if (fc_check != 0)
		fc_check = 10 - fc_check;
	return fc_check;
}

/*
 * ean2isn --- 尝试将ean13号码转换为UPC/ISxN号码。
 *			   这不会验证有效的校验位。
 *
 * 如果errorOK为false，则在ean13不正确时报告有用的错误信息。
 * 如果errorOK为true，则对于不良输入仅返回“false”。
 */
static bool fc_ean2isn(ean13 fc_ean, bool fc_errorOK, ean13 *fc_result, enum isn_type fc_accept)
{
	enum isn_type fc_type = INVALID;

	char		fc_buf[MAXEAN13LEN + 1];
	char	   *fc_aux;
	unsigned	fc_digval;
	unsigned	fc_search;
	ean13		fc_ret = fc_ean;

	fc_ean >>= 1;
	/* 验证它是否在EAN13范围内 */
	if (fc_ean > UINT64CONST(9999999999999))
		goto eantoobig;

	/* 转换号码 */
	fc_search = 0;
	fc_aux = fc_buf + 13;
	*fc_aux = '\0';				/* 终止字符串；aux指向最后一个数字 */
	do
	{
		fc_digval = (unsigned) (fc_ean % 10); /* 获取十进制值 */
		fc_ean /= 10;				/* 获取下一个数字 */
		*--fc_aux = (char) (fc_digval + '0'); /* 转换为ascii并存储 */
	} while (fc_ean && fc_search++ < 12);
	while (fc_search++ < 12)
		*--fc_aux = '0';			/* 用'0'填充剩余的EAN13 */

	/* 找出数据类型： */
	if (strncmp("978", fc_buf, 3) == 0)
	{							/* ISBN */
		fc_type = ISBN;
	}
	else if (strncmp("977", fc_buf, 3) == 0)
	{							/* ISSN */
		fc_type = ISSN;
	}
	else if (strncmp("9790", fc_buf, 4) == 0)
	{							/* ISMN */
		fc_type = ISMN;
	}
	else if (strncmp("979", fc_buf, 3) == 0)
	{							/* ISBN-13 */
		fc_type = ISBN;
	}
	else if (*fc_buf == '0')
	{							/* UPC */
		fc_type = UPC;
	}
	else
	{
		fc_type = EAN13;
	}
	if (fc_accept != ANY && fc_accept != EAN13 && fc_accept != fc_type)
		goto eanwrongtype;

	*fc_result = fc_ret;
	return true;

eanwrongtype:
	if (!fc_errorOK)
	{
		if (fc_type != EAN13)
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
							isn_names[fc_type], isn_names[fc_accept], fc_buf)));
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("cannot cast %s to %s for number: \"%s\"",
							isn_names[fc_type], isn_names[fc_accept], fc_buf)));
		}
	}
	return false;

eantoobig:
	if (!fc_errorOK)
	{
		char		fc_eanbuf[64];

		/*
		 * 单独格式化号码，以保持机器相关格式
		 * 代码不在可翻译的消息文本中
		 */
		snprintf(fc_eanbuf, sizeof(fc_eanbuf), EAN13_FORMAT, fc_ean);
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value \"%s\" is out of range for %s type",
						fc_eanbuf, isn_names[fc_type])));
	}
	return false;
}

/*
 * ean2UPC/ISxN --- 就地将标准化的EAN13字符串转换为相应的
 *					UPC/ISxN字符串号码。假设输入字符串已标准化。
 */
static inline void fc_ean2ISBN(char *fc_isn)
{
	char	   *fc_aux;
	unsigned	fc_check;

	/*
	 * 这个数字应以此格式出现：978-0-000-00000-0 或可能是一个
	 * ISBN-13 数字，979-...，它没有缩短表示形式。如果可能，输出简短版本。
	 */
	if (strncmp("978-", fc_isn, 4) == 0)
	{
		/* 去掉第一部分并计算新的校验位 */
		fc_hyphenate(fc_isn, fc_isn + 4, NULL, NULL);
		fc_check = fc_weight_checkdig(fc_isn, 10);
		fc_aux = strchr(fc_isn, '\0');
		while (!isdigit((unsigned char) *--fc_aux));
		if (fc_check == 10)
			*fc_aux = 'X';
		else
			*fc_aux = fc_check + '0';
	}
}

static inline void fc_ean2ISMN(char *fc_isn)
{
	/* 这个数字应以此格式出现：979-0-000-00000-0 */
	/* 只需去掉第一部分，并将第一个数字（'0'）更改为'M' */
	fc_hyphenate(fc_isn, fc_isn + 4, NULL, NULL);
	fc_isn[0] = 'M';
}

static inline void fc_ean2ISSN(char *fc_isn)
{
	unsigned	fc_check;

	/* 这个数字应以此格式出现：977-0000-000-00-0 */
	/* 去掉第一部分，裁剪并计算新的校验位 */
	fc_hyphenate(fc_isn, fc_isn + 4, NULL, NULL);
	fc_check = fc_weight_checkdig(fc_isn, 8);
	if (fc_check == 10)
		fc_isn[8] = 'X';
	else
		fc_isn[8] = fc_check + '0';
	fc_isn[9] = '\0';
}

static inline void fc_ean2UPC(char *fc_isn)
{
	/* 这个数字应以此格式出现：000-000000000-0 */
	/* 去掉第一部分，裁剪并去掉连字符 */
	fc_dehyphenate(fc_isn, fc_isn + 1);
	fc_isn[12] = '\0';
}

/*
 * ean2* --- 将一串数字转换为ean13数字。
 *			  假设输入字符串仅包含数字
 *			  并且在ean13的范围内。
 *
 * 返回该字符串的ean13值。
 */
static ean13
fc_str2ean(const char *fc_num)
{
	ean13		fc_ean = 0;		/* 当前ean */

	while (*fc_num)
	{
		if (isdigit((unsigned char) *fc_num))
			fc_ean = 10 * fc_ean + (*fc_num - '0');
		fc_num++;
	}
	return (fc_ean << 1);			/* 也为标志留出空间 */
}

/*
 * ean2string --- 尝试将ean13数字转换为带连字符的字符串。
 *				  假设结果中有足够的空间容纳
 *				  字符串（最大 MAXEAN13LEN+1 字节）
 *				  这不会验证有效的校验位。
 *
 * 如果shortType为true，则返回的字符串为旧的ISxN短格式。
 * 如果errorOK为false，出现错误时报告有用的错误消息。
 * 如果errorOK为true，对输入错误的情况下仅返回“false”。
 */
static bool fc_ean2string(ean13 fc_ean, bool fc_errorOK, char *fc_result, bool fc_shortType)
{
	const char *(*fc_TABLE)[2];
	const unsigned (*fc_TABLE_index)[2];
	enum isn_type fc_type = INVALID;

	char	   *fc_aux;
	unsigned	fc_digval;
	unsigned	fc_search;
	char		fc_valid = '\0';	/* 这个数字最初是否以有效的
								 * 校验位编写？ */

	fc_TABLE_index = ISBN_index;

	if ((fc_ean & 1) != 0)
		fc_valid = '!';
	fc_ean >>= 1;
	/* 验证它是否在EAN13范围内 */
	if (fc_ean > UINT64CONST(9999999999999))
		goto eantoobig;

	/* 转换号码 */
	fc_search = 0;
	fc_aux = fc_result + MAXEAN13LEN;
	*fc_aux = '\0';				/* 终止字符串；aux指向最后一个数字 */
	*--fc_aux = fc_valid;				/* 为校验位无效但已修正的数字附加'!' */
	do
	{
		fc_digval = (unsigned) (fc_ean % 10); /* 获取十进制值 */
		fc_ean /= 10;				/* 获取下一个数字 */
		*--fc_aux = (char) (fc_digval + '0'); /* 转换为ascii并存储 */
		if (fc_search == 0)
			*--fc_aux = '-';		/* 校验位始终存在 */
	} while (fc_ean && fc_search++ < 13);
	while (fc_search++ < 13)
		*--fc_aux = '0';			/* 用'0'填充剩余的EAN13 */

	/* 字符串应在此格式中：???DDDDDDDDDDDD-D" */
	fc_search = fc_hyphenate(fc_result, fc_result + 3, EAN13_range, EAN13_index);

	/* 验证它是否是逻辑上有效的EAN13 */
	if (fc_search == 0)
	{
		fc_search = fc_hyphenate(fc_result, fc_result + 3, NULL, NULL);
		goto okay;
	}

	/* 找出需要什么类型的连字符： */
	if (strncmp("978-", fc_result, fc_search) == 0)
	{							/* ISBN -13 978范围 */
		/* 字符串应在此格式中：978-??000000000-0" */
		fc_type = ISBN;
		fc_TABLE = ISBN_range;
		fc_TABLE_index = ISBN_index;
	}
	else if (strncmp("977-", fc_result, fc_search) == 0)
	{							/* ISSN */
		/* 字符串应在此格式中：977-??000000000-0" */
		fc_type = ISSN;
		fc_TABLE = ISSN_range;
		fc_TABLE_index = ISSN_index;
	}
	else if (strncmp("979-0", fc_result, fc_search + 1) == 0)
	{							/* ISMN */
		/* 字符串应在此格式中：979-0?000000000-0" */
		fc_type = ISMN;
		fc_TABLE = ISMN_range;
		fc_TABLE_index = ISMN_index;
	}
	else if (strncmp("979-", fc_result, fc_search) == 0)
	{							/* ISBN-13 979范围 */
		/* 字符串应在此格式中：979-??000000000-0" */
		fc_type = ISBN;
		fc_TABLE = ISBN_range_new;
		fc_TABLE_index = ISBN_index_new;
	}
	else if (*fc_result == '0')
	{							/* UPC */
		/* 字符串应在此格式中：000-00000000000-0" */
		fc_type = UPC;
		fc_TABLE = UPC_range;
		fc_TABLE_index = UPC_index;
	}
	else
	{
		fc_type = EAN13;
		fc_TABLE = NULL;
		fc_TABLE_index = NULL;
	}

	/* 验证它是否是逻辑上有效的EAN13/UPC/ISxN */
	fc_digval = fc_search;
	fc_search = fc_hyphenate(fc_result + fc_digval, fc_result + fc_digval + 2, fc_TABLE, fc_TABLE_index);

	/* 验证它是否是有效的EAN13 */
	if (fc_search == 0)
	{
		fc_search = fc_hyphenate(fc_result + fc_digval, fc_result + fc_digval + 2, NULL, NULL);
		goto okay;
	}

okay:
	/* 转换为旧的短类型： */
	if (fc_shortType)
		switch (fc_type)
		{
			case ISBN:
				fc_ean2ISBN(fc_result);
				break;
			case ISMN:
				fc_ean2ISMN(fc_result);
				break;
			case ISSN:
				fc_ean2ISSN(fc_result);
				break;
			case UPC:
				fc_ean2UPC(fc_result);
				break;
			default:
				break;
		}
	return true;

eantoobig:
	if (!fc_errorOK)
	{
		char		fc_eanbuf[64];

		/*
		 * 单独格式化号码，以保持机器相关格式
		 * 代码不在可翻译的消息文本中
		 */
		snprintf(fc_eanbuf, sizeof(fc_eanbuf), EAN13_FORMAT, fc_ean);
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value \"%s\" is out of range for %s type",
						fc_eanbuf, isn_names[fc_type])));
	}
	return false;
}

/*
 * string2ean --- 尝试将字符串解析为ean13。
 *
 * 如果errorOK为false，当字符串有误时输出有用的错误信息。
 * 如果errorOK为true，对于无效输入仅返回“false”。
 *
 * 如果输入字符串以'!'结尾，它将始终被视为无效
 * （即使校验位是有效的）
 */
static bool fc_string2ean(const char *fc_str, bool fc_errorOK, ean13 *fc_result,
		   enum isn_type fc_accept)
{
	bool		fc_digit,
				fc_last;
	char		fc_buf[17] = "                ";
	char	   *fc_aux1 = fc_buf + 3; /* 保留第一部分的空间，以防
								 * 需要 */
	const char *fc_aux2 = fc_str;
	enum isn_type fc_type = INVALID;
	unsigned	fc_check = 0,
				fc_rcheck = (unsigned) -1;
	unsigned	fc_length = 0;
	bool		fc_magic = false,
				fc_valid = true;

	/* 识别并验证数字： */
	while (*fc_aux2 && fc_length <= 13)
	{
		fc_last = (*(fc_aux2 + 1) == '!' || *(fc_aux2 + 1) == '\0'); /* 是最后一个字符 */
		fc_digit = (isdigit((unsigned char) *fc_aux2) != 0);	/* 当前字符
														 * 是数字吗？ */
		if (*fc_aux2 == '?' && fc_last)	/* 如果是'?' 自动计算校验位 */
			fc_magic = fc_digit = true;
		if (fc_length == 0 && (*fc_aux2 == 'M' || *fc_aux2 == 'm'))
		{
			/* 这里只能是ISMN */
			if (fc_type != INVALID)
				goto eaninvalid;
			fc_type = ISMN;
			*fc_aux1++ = 'M';
			fc_length++;
		}
		else if (fc_length == 7 && (fc_digit || *fc_aux2 == 'X' || *fc_aux2 == 'x') && fc_last)
		{
			/* 这里只能是ISSN */
			if (fc_type != INVALID)
				goto eaninvalid;
			fc_type = ISSN;
			*fc_aux1++ = toupper((unsigned char) *fc_aux2);
			fc_length++;
		}
		else if (fc_length == 9 && (fc_digit || *fc_aux2 == 'X' || *fc_aux2 == 'x') && fc_last)
		{
			/* 这里只能是ISBN和ISMN */
			if (fc_type != INVALID && fc_type != ISMN)
				goto eaninvalid;
			if (fc_type == INVALID)
				fc_type = ISBN;	/* ISMN必须以'M'开始 */
			*fc_aux1++ = toupper((unsigned char) *fc_aux2);
			fc_length++;
		}
		else if (fc_length == 11 && fc_digit && fc_last)
		{
			/* 这里只能是UPC */
			if (fc_type != INVALID)
				goto eaninvalid;
			fc_type = UPC;
			*fc_aux1++ = *fc_aux2;
			fc_length++;
		}
		else if (*fc_aux2 == '-' || *fc_aux2 == ' ')
		{
			/* 跳过，我们可以验证但我认为没必要 */
		}
		else if (*fc_aux2 == '!' && *(fc_aux2 + 1) == '\0')
		{
			/* 找到无效校验位后缀，设置它 */
			if (!fc_magic)
				fc_valid = false;
			fc_magic = true;
		}
		else if (!fc_digit)
		{
			goto eaninvalid;
		}
		else
		{
			*fc_aux1++ = *fc_aux2;
			if (++fc_length > 13)
				goto eantoobig;
		}
		fc_aux2++;
	}
	*fc_aux1 = '\0';				/* 终止字符串 */

	/* 找到当前校验位值 */
	if (fc_length == 13)
	{
		/* 这里只能是EAN13 */
		if (fc_type != INVALID)
			goto eaninvalid;
		fc_type = EAN13;
		fc_check = fc_buf[15] - '0';
	}
	else if (fc_length == 12)
	{
		/* 这里只能是UPC */
		if (fc_type != UPC)
			goto eaninvalid;
		fc_check = fc_buf[14] - '0';
	}
	else if (fc_length == 10)
	{
		if (fc_type != ISBN && fc_type != ISMN)
			goto eaninvalid;
		if (fc_buf[12] == 'X')
			fc_check = 10;
		else
			fc_check = fc_buf[12] - '0';
	}
	else if (fc_length == 8)
	{
		if (fc_type != INVALID && fc_type != ISSN)
			goto eaninvalid;
		fc_type = ISSN;
		if (fc_buf[10] == 'X')
			fc_check = 10;
		else
			fc_check = fc_buf[10] - '0';
	}
	else
		goto eaninvalid;

	if (fc_type == INVALID)
		goto eaninvalid;

	/* 获取真实的校验位值，验证并转换为ean13： */
	if (fc_accept == EAN13 && fc_type != fc_accept)
		goto eanwrongtype;
	if (fc_accept != ANY && fc_type != EAN13 && fc_type != fc_accept)
		goto eanwrongtype;
	switch (fc_type)
	{
		case EAN13:
			fc_valid = (fc_valid && ((fc_rcheck = fc_checkdig(fc_buf + 3, 13)) == fc_check || fc_magic));
			/* 现在获取EAN13的子类型： */
			if (fc_buf[3] == '0')
				fc_type = UPC;
			else if (strncmp("977", fc_buf + 3, 3) == 0)
				fc_type = ISSN;
			else if (strncmp("978", fc_buf + 3, 3) == 0)
				fc_type = ISBN;
			else if (strncmp("9790", fc_buf + 3, 4) == 0)
				fc_type = ISMN;
			else if (strncmp("979", fc_buf + 3, 3) == 0)
				fc_type = ISBN;
			if (fc_accept != EAN13 && fc_accept != ANY && fc_type != fc_accept)
				goto eanwrongtype;
			break;
		case ISMN:
			memcpy(fc_buf, "9790", 4); /* 这还不确定，目前ISMN
									 * 仅为9790 */
			fc_valid = (fc_valid && ((fc_rcheck = fc_checkdig(fc_buf, 13)) == fc_check || fc_magic));
			break;
		case ISBN:
			memcpy(fc_buf, "978", 3);
			fc_valid = (fc_valid && ((fc_rcheck = fc_weight_checkdig(fc_buf + 3, 10)) == fc_check || fc_magic));
			break;
		case ISSN:
			memcpy(fc_buf + 10, "00", 2);	/* 以00作为正常发行为
										 * 出版代码附加 */
			memcpy(fc_buf, "977", 3);
			fc_valid = (fc_valid && ((fc_rcheck = fc_weight_checkdig(fc_buf + 3, 8)) == fc_check || fc_magic));
			break;
		case UPC:
			fc_buf[2] = '0';
			fc_valid = (fc_valid && ((fc_rcheck = fc_checkdig(fc_buf + 2, 13)) == fc_check || fc_magic));
		default:
			break;
	}

	/* 修正校验位： */
	for (fc_aux1 = fc_buf; *fc_aux1 && *fc_aux1 <= ' '; fc_aux1++);
	fc_aux1[12] = fc_checkdig(fc_aux1, 13) + '0';
	fc_aux1[13] = '\0';

	if (!fc_valid && !fc_magic)
		goto eanbadcheck;

	*fc_result = fc_str2ean(fc_aux1);
	*fc_result |= fc_valid ? 0 : 1;
	return true;

eanbadcheck:
	if (g_weak)
	{							/* 弱输入模式已激活： */
		/* 设置“输入时无效校验位”标志 */
		*fc_result = fc_str2ean(fc_aux1);
		*fc_result |= 1;
		return true;
	}

	if (!fc_errorOK)
	{
		if (fc_rcheck == (unsigned) -1)
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid %s number: \"%s\"",
							isn_names[fc_accept], fc_str)));
		}
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("invalid check digit for %s number: \"%s\", should be %c",
							isn_names[fc_accept], fc_str, (fc_rcheck == 10) ? ('X') : (fc_rcheck + '0'))));
		}
	}
	return false;

eaninvalid:
	if (!fc_errorOK)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for %s number: \"%s\"",
						isn_names[fc_accept], fc_str)));
	return false;

eanwrongtype:
	if (!fc_errorOK)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("cannot cast %s to %s for number: \"%s\"",
						isn_names[fc_type], isn_names[fc_accept], fc_str)));
	return false;

eantoobig:
	if (!fc_errorOK)
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("value \"%s\" is out of range for %s type",
						fc_str, isn_names[fc_accept])));
	return false;
}

/*----------------------------------------------------------
 * 导出例程。
 *---------------------------------------------------------*/

void		_PG_init(void);

void _PG_init(void)
{
	if (ISN_DEBUG)
	{
		if (!fc_check_table(EAN13_range, EAN13_index))
			elog(ERROR, "EAN13 failed check");
		if (!fc_check_table(ISBN_range, ISBN_index))
			elog(ERROR, "ISBN failed check");
		if (!fc_check_table(ISMN_range, ISMN_index))
			elog(ERROR, "ISMN failed check");
		if (!fc_check_table(ISSN_range, ISSN_index))
			elog(ERROR, "ISSN failed check");
		if (!fc_check_table(UPC_range, UPC_index))
			elog(ERROR, "UPC failed check");
	}
}

/* isn_out
 */
PG_FUNCTION_INFO_V1(isn_out);
Datum isn_out(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	char	   *fc_result;
	char		fc_buf[MAXEAN13LEN + 1];

	(void) fc_ean2string(fc_val, false, fc_buf, true);

	fc_result = pstrdup(fc_buf);
	PG_RETURN_CSTRING(fc_result);
}

/* ean13_out
 */
PG_FUNCTION_INFO_V1(ean13_out);
Datum ean13_out(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	char	   *fc_result;
	char		fc_buf[MAXEAN13LEN + 1];

	(void) fc_ean2string(fc_val, false, fc_buf, false);

	fc_result = pstrdup(fc_buf);
	PG_RETURN_CSTRING(fc_result);
}

/* ean13_in
 */
PG_FUNCTION_INFO_V1(ean13_in);
Datum ean13_in(PG_FUNCTION_ARGS)
{
	const char *fc_str = PG_GETARG_CSTRING(0);
	ean13		fc_result;

	(void) fc_string2ean(fc_str, false, &fc_result, EAN13);
	PG_RETURN_EAN13(fc_result);
}

/* isbn_in
 */
PG_FUNCTION_INFO_V1(isbn_in);
Datum isbn_in(PG_FUNCTION_ARGS)
{
	const char *fc_str = PG_GETARG_CSTRING(0);
	ean13		fc_result;

	(void) fc_string2ean(fc_str, false, &fc_result, ISBN);
	PG_RETURN_EAN13(fc_result);
}

/* ismn_in
 */
PG_FUNCTION_INFO_V1(ismn_in);
Datum ismn_in(PG_FUNCTION_ARGS)
{
	const char *fc_str = PG_GETARG_CSTRING(0);
	ean13		fc_result;

	(void) fc_string2ean(fc_str, false, &fc_result, ISMN);
	PG_RETURN_EAN13(fc_result);
}

/* issn_in
 */
PG_FUNCTION_INFO_V1(issn_in);
Datum issn_in(PG_FUNCTION_ARGS)
{
	const char *fc_str = PG_GETARG_CSTRING(0);
	ean13		fc_result;

	(void) fc_string2ean(fc_str, false, &fc_result, ISSN);
	PG_RETURN_EAN13(fc_result);
}

/* upc_in
 */
PG_FUNCTION_INFO_V1(upc_in);
Datum upc_in(PG_FUNCTION_ARGS)
{
	const char *fc_str = PG_GETARG_CSTRING(0);
	ean13		fc_result;

	(void) fc_string2ean(fc_str, false, &fc_result, UPC);
	PG_RETURN_EAN13(fc_result);
}

/* 转换函数
*/
PG_FUNCTION_INFO_V1(isbn_cast_from_ean13);
Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	ean13		fc_result;

	(void) fc_ean2isn(fc_val, false, &fc_result, ISBN);

	PG_RETURN_EAN13(fc_result);
}

PG_FUNCTION_INFO_V1(ismn_cast_from_ean13);
Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	ean13		fc_result;

	(void) fc_ean2isn(fc_val, false, &fc_result, ISMN);

	PG_RETURN_EAN13(fc_result);
}

PG_FUNCTION_INFO_V1(issn_cast_from_ean13);
Datum issn_cast_from_ean13(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	ean13		fc_result;

	(void) fc_ean2isn(fc_val, false, &fc_result, ISSN);

	PG_RETURN_EAN13(fc_result);
}

PG_FUNCTION_INFO_V1(upc_cast_from_ean13);
Datum upc_cast_from_ean13(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);
	ean13		fc_result;

	(void) fc_ean2isn(fc_val, false, &fc_result, UPC);

	PG_RETURN_EAN13(fc_result);
}


/* is_valid - 如果设置了“输入时无效校验位”则返回false
 */
PG_FUNCTION_INFO_V1(is_valid);
Datum is_valid(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);

	PG_RETURN_BOOL((fc_val & 1) == 0);
}

/* make_valid - 取消设置“输入时无效校验位”标志
 */
PG_FUNCTION_INFO_V1(make_valid);
Datum make_valid(PG_FUNCTION_ARGS)
{
	ean13		fc_val = PG_GETARG_EAN13(0);

	fc_val &= ~((ean13) 1);
	PG_RETURN_EAN13(fc_val);
}

/* 此函数暂时设置弱输入标志
 * （以放宽对校验位接受的严格性）
 * 它是一个辅助函数，不打算被用于!!
 */
PG_FUNCTION_INFO_V1(accept_weak_input);
Datum accept_weak_input(PG_FUNCTION_ARGS)
{
#ifdef ISN_WEAK_MODE
	g_weak = PG_GETARG_BOOL(0);
#else
	/* 功能无效 */
#endif							/* ISN_WEAK_MODE */
	PG_RETURN_BOOL(g_weak);
}

PG_FUNCTION_INFO_V1(weak_input_status);
Datum weak_input_status(PG_FUNCTION_ARGS)
{
	PG_RETURN_BOOL(g_weak);
}
